#define _BSD_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include </usr/include/ar.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>

#define STR_SIZE sizeof("rwxrwxrwx")
#define FP_SPECIAL 1 

//create the archive if not found
void crtArc(char *name)
{
	int opened = 0;
	opened = open(name, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
	write(name, ARMAG, SARMAG);
}

//append named files to archive
void appArc(int argc, char **argv)
{
	int i = 0;
	int j = 0;
	int openAr = 0;
	int openFl = 0;
	char buffer[1024];
	char *aName = argv[2];

	struct stat info;
	struct ar_hdr arHd;

	openAr = open(aName, O_RDONLY);

	//if it didn't exist create it
	if(openAr == -1) {
		crtArc(aName);
	}
	close(aName);

	openAr = open(aName, O_WRONLY | O_APPEND);

	for(i = 0; (i + 3) < argc; i++) {
		openFl = open(argv[i + 3], O_RDONLY);

		//if file not found
		if(openFl == -1) {
			printf("Error File not found");
		}
		if(stat(argv[i + 3], &info) == -1) {
			perror("Stat error");
			exit(-1);
		}

		//convert to strings
		sprintf(arHd.ar_name, "%s", argv[i + 3]);
		sprintf(arHd.ar_date, "%1d", (long) info.st_mtime);
		sprintf(arHd.ar_uid, "%1d", (long) info.st_uid);
		sprintf(arHd.ar_gid, "%1d", (long) info.st_gid);
		sprintf(arHd.ar_mode, "%1o", (unsigned long) info.st_mode);
		sprintf(arHd.ar_size, "%11d", (long long) info.st_size);
		sprintf(arHd.ar_fmag, "%s", "`\n");

		//null removal and space padd name
		while(arHd.ar_name != '\0') {
			j++;
		}
		arHd.ar_name[j] = '/';
		j++;
		while(j < 16) {
			arHd.ar_name[j] = ' ';
			j++;
		}
		j = 0;

		//null removal and space padd date
		while(arHd.ar_date[j] != '\0'){
			j++;
		}
		while(j < 12){
			arHd.ar_date[j] = ' ';
			j++;
		}
		j = 0;

		//null removal and space padd user id
		while(arHd.ar_uid[j] != '\0'){
			j++;
		}
		while(j < 6){
			arHd.ar_uid[j] = ' ';
			j++;
		}
		j = 0;

		//null removal and space padd group id
		while(arHd.ar_gid[j] != '\0'){
			j++;
		}
		while(j < 6){
			arHd.ar_gid[j] = ' ';
			j++;
		}
		j = 0;

		//null removal and space padd mode
		while(arHd.ar_mode[j] != '\0'){
			j++;
		}
		while(j < 8){
			arHd.ar_mode[j] = ' ';
			j++;
		}
		j = 0;

		//null removal and space padd size
		while(arHd.ar_size[j] != '\0'){
			j++;
		}
		while(j < 10){
			arHd.ar_size[j] = ' ';
			j++;
		}
		j = 0;

		//null removal and space padd fmag
		while(arHd.ar_fmag[j] != '\0'){
			j++;
		}
		while(j < 2){
			arHd.ar_fmag[j] = ' ';
			j++;
		}
		j = 0;

		//write headers
		write(aName, arHd.ar_name, 16);
		write(aName, arHd.ar_date, 12);
		write(aName, arHd.ar_uid, 6);
		write(aName, arHd.ar_gid, 6);
		write(aName, arHd.ar_mode, 10);
		write(aName, arHd.ar_fmag, 2);

		j = read(openFl, buffer, 1024);
		while(0 < j) {
			write(aName, buffer, j);
			j = read(aName, buffer, 1024);
		}
		close(openFl);
	}
	close(openAr);
}

//extracts named files
void extract(int argc, char **argv)
{
	/*
	int nameUsed = 0;

	nameUsed = chkArc(aName);

	if(!nameUsed){

		nameUsed = chkF(name);
		if(!nameUsed){

		} else {
			printf("The named file already exists.");
		}
	} else {
		printf("The archive specified doesn't exist.");
	}
	*/
}

//prints a consice table of contents
void cCont(int argc, char **argv)
{
	int i = 0;
	int j = 0;
	int k = 0;
	int fp = 0;
	int fSize = 0;
	int openAr = 0;
	char buffer[60];
	char *aName = argv[2];

	//get headers
	char *fName = (char*) malloc(16 * sizeof(char));
	char *date = (char*) malloc(12 * sizeof(char));
	char *uid = (char*) malloc(6 * sizeof(char));
	char * gid = (char*) malloc(6 * sizeof(char));
	char * mode = (char*) malloc(8 * sizeof(char));
	char * size = (char*) malloc(10 * sizeof(char));
	char * fmag = (char*) malloc(2 * sizeof(char));

	openAr = open(aName, O_RDONLY);
	lseek(openAr, 8, SEEK_CUR);
	printf("\n");

	//while not eof
	while(i != -1) {
		fp = read(aName, buffer, 60);

		//nothing read
		if(fp != 60) {
			free(fName);
			free(date);
			free(uid);
			free(gid);
			free(mode);
			free(size);
			close(aName);
			exit(-1);
		}

		//get file name
		while(j < 16) {
			if(buffer[j] == '/') {
				fName[k] = ' ';
				k++;
				j++;
			}
			fName[k] = buffer[j];
			k++;
			j++;
		}
		fName += '\0';
		k = 0;
		while(j < 28) {
			date[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		date += '\0';
		while(j < 34) {
			uid[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		uid += '\0';
		while(j < 40) {
			gid[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		gid += '\0';
		while(j < 48) {
			mode[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		size += '\0';
		while(j < 60) {
			fmag[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		j = 0;
		fmag += '\0';

		printf("%s\n", fName);
		i = lseek(openAr, atol(size), SEEK_CUR);
	}
}

//changes permissions
char *permission(mode_t perm, int flags)
{
	/* Return ls(1)-style string for file permissions mask */
	static char str[STR_SIZE];
	snprintf(str, STR_SIZE, "%c%c%c%c%c%c%c%c%c",
	         (perm & S_IRUSR) ? 'r' : '-', (perm & S_IWUSR) ? 'w' : '-',
	         (perm & S_IXUSR) ?
	         (((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 's' : 'x') :
	         (((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
	         (perm & S_IRGRP) ? 'r' : '-', (perm & S_IWGRP) ? 'w' : '-',
	         (perm & S_IXGRP) ?
	         (((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 's' : 'x') :
	         (((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
	         (perm & S_IROTH) ? 'r' : '-', (perm & S_IWOTH) ? 'w' : '-',
	         (perm & S_IXOTH) ?
	         (((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 't' : 'x') :
	         (((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 'T' : '-'));
	return str;
}

//convert from ascii to octal
int octConv(char* s)
{
	int p = 1;
	int num = 0;
	int sLen = 0;
	int i;
	
	for(i = 0; s[i] != '\0'; i++){
		sLen++;
	}
	
	i = sLen - 1;
	
	while(i >= 0){
		if(s[i] != ' '){
			num += (p * (s[i] - '0'));
			p *= 8;
		}
		
		i--;
	}
	
	return num;
}

//prints a verbose table of contents
void vCont(int argc, char **argv)
{
	int i = 0;
	int j = 0;
	int k = 0;
	int fp;
	int fSize;
	int openAr;
	long timer;
	char buffer[60];
	char *aName = argv[2];
	char *currDate = (char*) malloc(20 * sizeof(char));
	

	//get headers
	char *fName = (char*) malloc(16 * sizeof(char));
	char* date = (char*) malloc(12 * sizeof(char));
	char* uid = (char*) malloc(6 * sizeof(char));
	char* gid = (char*) malloc(6 * sizeof(char));
	char* mode = (char*) malloc(8 * sizeof(char));
	char* size = (char*) malloc(10 * sizeof(char));
	char* fmag = (char*) malloc(2 * sizeof(char));

	openAr = open(aName, O_RDONLY);
	lseek(openAr, 8, SEEK_CUR);

	while(i != -1){
		fp = read(openAr, buffer, 60);

		if(fp != 60){
			printf("\n\n");
			free(fName);
			free(date);
			free(uid);
			free(gid);
			free(mode);
			free(size);
			close(openAr);
			exit(-1);
		}

		//get filename
		while(j < 16){
			if(buffer[j] == '/'){
				fName[k] = ' ';
				k++;
				j++;
			}
			fName[k] = buffer[j];
			k++;
			j++;
		}
		fName += '\0';
		k = 0;
		while(j < 28){
			date[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		date += '\0';
		while(j < 34){
			uid[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		uid = uid + '\0';
		while(j < 40){
			gid[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		gid = gid + '\0';
		while(j < 48){
			mode[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		mode += '\0';
		while(j < 58){
			size[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		size = size + '\0';
		while(j < 60){
			fmag[k] = buffer[j];
			k++;
			j++;
		}
		k = 0;
		j = 0;
		fmag = fmag + '\0';

		printf("\n%s ", permission(octConv(mode), 0));
		printf("%d/", atoi(uid));
		printf("%d", atoi(gid));
		printf("%7lu", atol(size));

		//std time
		timer = atol(date);
		strftime(currDate, 20, "%b %d %H:%M %Y", localtime(&timer));
		printf(" %s", currDate);
		printf(" %s", fName);

		i = lseek(openAr, atol(size), SEEK_CUR);
	}
}

//deletes named files
void del(int argc, char **argv)
{
	char* aName = argv[2];
	
	int fp = 0;
	int i = 0;
	int j = 0;
	int k = 0;
	int openAr;
	int newAr;
	int check = 0;
	char buffer1[60];
	char buffer2[1024];
	char *sFname = (char*) malloc(16 * sizeof(char));
	char *delF = (char*) malloc(2 * sizeof(char));
	delF[0] = '\n';
	delF[1] = '\0';

	//get headers
	char* fName = (char*) malloc(16 * sizeof(char));
	char* date = (char*) malloc(12 * sizeof(char));
	char* uid = (char*) malloc(6 * sizeof(char));
	char* gid = (char*) malloc(6 * sizeof(char));
	char* mode = (char*) malloc(8 * sizeof(char));
	char* size = (char*) malloc(10 * sizeof(char));
	char* fmag = (char*) malloc(2 * sizeof(char));

	openAr = open(aName, O_RDONLY);
	unlink(aName);

	//create new archive from old
	newAr = open(aName, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
	write(newAr, ARMAG, SARMAG);
	lseek(openAr, 8, SEEK_CUR);

	//itterate through archive and skip files marked for deletion
	while(fp != -1){
		fp = read(openAr, buffer1, 60);

		if(fp != 60){
			free(fName);
			free(date);
			free(uid);
			free(gid);
			free(mode);
			free(size);
			free(sFname);
			free(delF);
			close(newAr);
			close(openAr);

			exit(-1);
		}

		//retrive info from buffer
		while(j < 16){
			if(buffer1[j] == '/'){
				fName[k] = ' ';
				k++;
				j++;
			}
			fName[k] = buffer1[j];
			k++;
			j++;
		}
		k = 0;
		while(j < 28){
			date[k] = buffer1[j];
			k++;
			j++;
		}
		k = 0;
		while(j < 34){
			uid[k] = buffer1[j];
			k++;
			j++;
		}
		k = 0;
		while(j < 40){
			gid[k] = buffer1[j];
			k++;
			j++;
		}
		k = 0;
		while(j < 48){
			mode[k] = buffer1[j];
			k++;
			j++;
		}
		k = 0;
		while(j < 58){
			size[k] = buffer1[j];
			k++;
			j++;
		}
		k = 0;
		while(j < 60){
			fmag[k] = buffer1[j];
			k++;
			j++;
		}
		k = 0;
		j = 0;

		sFname = strcpy(sFname, fName);

		while((i+3) < argc){
			if(((strcmp(sFname, argv[i+3])) == 1) && (check != 1)) {
				argv[i+3] = strcpy(argv[i+3], delF);
				lseek(openAr, atol(size), SEEK_CUR);
				check = 1;
			}
			i++;
		}
		i = 0;

		if(check != 1){
			write(newAr, fName, 16);
			write(newAr, date, 12);
			write(newAr, uid, 6);
			write(newAr, gid, 6);
			write(newAr, mode, 8);
			write(newAr, size, 10);
			write(newAr, fmag, 2);

			fp = read(openAr, buffer2, atol(size));
			write(newAr, buffer2, fp);
		}
		check = 0;
	}
}

//append regular files in current dir
void appRg(int argc, char **argv)
{
	DIR *directory;
	struct dirent *dirEntry;
	struct stat node;

	char *aName = argv[2] + '\0';
	char *dirPath;
	char *fileName;
	char name[PATH_MAX];
	char buf[100];

	sprintf(aName, "%s\0", argv[2]);
	dirPath = getcwd(buf, 100);
	directory = opendir(dirPath);

	if (directory <= 0){
		printf("Error couldn't find directory\n");
		exit(1);
	}

	while ((dirEntry = readdir(directory)) != 0){
		sprintf(name, "%s/%s", dirPath, dirEntry -> d_name); 
		lstat (name, &node);
		fileName = (char *) (dirEntry -> d_name);

		/*Check if file is Regular*/
		if (S_ISREG(node.st_mode))
			if(!(strcmp(aName, dirEntry -> d_name))){
				argv[3] = fileName;
				argc = 4;
				appArc(argc, argv);
			}
	}
}

//main
int main(int argc, char **argv)
{
	if(argc != 0) {
		switch (argv[1][1]){
		case 'q':
			appArc(argc, argv);
			break;
		case 'x':
			extract(argc, argv);
			break;
		case 't':
			cCont(argc, argv);
			break;
		case 'v':
			vCont(argc, argv);
			break;
		case 'd':
			del(argc, argv);
		case 'A':
			appRg(argc, argv);
			break;
		default:
			printf("Invalid input detected.");
			break;
		}
	}
	return 0;
}